package com.ruoyi.web.controller.system;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cems.service.ILanXinService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.RestTemplateUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 登录验证
 *
 * @author ruoyi
 */
@RestController
@Api(description="登录管理")
@Slf4j
public class SysLoginController
{
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private ILanXinService lanXinService;

    @Value("${sso.host}")
    private String host;

    @Value("${sso.client_id}")
    private String clientId;

    @Value("${sso.grant_type}")
    private String grantType;

    @Value("${sso.client_secret}")
    private String clientSecret;

    @Value("${sso.redirect_uri}")
    private String redirectUri;

    @Value("${lxsso.host}")
    private String lxHost;

    @Value("${lxsso.gatewayHost}")
    private String lxGatewayHost;

    @Value("${lxsso.client_id}")
    private String lxClientId;

    @Value("${lxsso.grant_type}")
    private String lxGrantType;

    @Value("${lxsso.client_secret}")
    private String lxClientSecret;

    @Value("${lxsso.redirect_uri}")
    private String lxRedirectUri;

    @Autowired
    private ISysUserService userService;
    @Autowired
    private RedisCache redisCache;

    /**
     * 登录方法
     *
     * @param loginBody 登录信息
     * @return 结果
     */
    @PostMapping("/login")
    @ApiOperation("登录接口")
    public AjaxResult login(@RequestBody LoginBody loginBody)
    {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                loginBody.getUuid());
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    @GetMapping("/login/getCodeSsoUrl")
    @ApiOperation("获取code的url地址")
    public AjaxResult getCodeSsoUrl() {
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(host+"/authorize").append("?").append("client_id=").append(clientId).append("&response_type=code")
                .append("&redirect_uri=").append(redirectUri);
        return AjaxResult.success("操作成功", urlSb.toString());
    }
    @GetMapping("/login/getCodeLxSsoUrl")
    @ApiOperation("获取蓝信code的url地址")
    public AjaxResult getCodeLxSsoUrl() {
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(lxHost+"/oauth2/authorize").append("?").append("appid=").append(lxClientId).append("&response_type=code&scope=basic_userinfor&state=STATE")
                .append("&redirect_uri=").append(lxRedirectUri);
        return AjaxResult.success("操作成功", urlSb.toString());
    }
    @GetMapping("/login/getLxJsSdkConfig")
    @ApiOperation("获取蓝信JSAPI鉴权")
    public AjaxResult getLxJsSdkConfig(String url) {
        log.info("获取蓝信JSAPI鉴权url：{}",url);
        String jsApiToken = getJsApiToken();
        if(StringUtils.isBlank(jsApiToken)){
            return AjaxResult.error("获取jsApiToken失败");
        }
        Long timestamp = System.currentTimeMillis() / 1000;
        String nonce = UUID.randomUUID().toString();
        String stringToSign ="js_api_token="+jsApiToken+"&noncestr="+nonce+"&timestamp="+timestamp + "&url=" + url;
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return AjaxResult.error("签名生成失败");
        }
        md.update(stringToSign.getBytes());
        String signature = "";
        for (byte b : md.digest()){
            signature += String.format("%02x", b);
        }
        log.info("获取蓝信JSAPI鉴权签名结果：{}",signature);
        Map<String, Object> data = new HashMap<>();
        data.put("appId", lxClientId);
        data.put("timestamp", timestamp);
        data.put("nonceStr", nonce);
        data.put("signature", signature);

        return AjaxResult.success("操作成功", data);
    }
    /**
     * 单点登录
     *
     * @param code 登录信息
     * @return 结果
     */
    @GetMapping("/login/sso")
    @ApiOperation("单点登录接口")
    public AjaxResult ssoLogin(@RequestParam String code, HttpServletResponse response)
    {
        AjaxResult ajax = AjaxResult.success();
        if(StringUtils.isBlank(code)){
            //code为空直接重定向sso，获取code
            StringBuilder urlSb = new StringBuilder();
            try {
                urlSb.append(host+"/authorize").append("?").append("client_id=").append(clientId).append("&response_type=code")
                        .append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            try {
                response.sendRedirect(urlSb.toString());
                return ajax;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        log.info("单点登录Code:{}", code);
        // 获取单点登录系统token
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(host+"/accessToken").append("?").append("client_id=").append(clientId).append("&grant_type=").append(grantType)
                .append("&client_secret=").append(clientSecret).append("&redirect_uri=").append(redirectUri)
                .append("&code=").append(code);
        log.info("单点登录url:{}", urlSb.toString());
        RestTemplate restTemplate = RestTemplateUtil.getRestTemplateWithTrustAllSSL();
        ResponseEntity<String> responseData = restTemplate.postForEntity(urlSb.toString(), null, String.class);
        log.info("单点登录获取token返回结果:{}", responseData.getBody());
        if(responseData.getBody() != null){
            String accessToken = JSONObject.parseObject(responseData.getBody()).getString("access_token");
            responseData = restTemplate.getForEntity(host + "/profile?access_token=" + accessToken, String.class);
            log.info("单点登录获取用户信息返回结果:{}", responseData.getBody());
            if(responseData.getBody() != null){
                JSONObject userInfo = JSONObject.parseObject(responseData.getBody()).getJSONObject("attributes");
                if(userInfo != null){
                    //生成本地token
                    String token = loginService.ssoLogin(userInfo.getString("account_no"));
                    ajax.put(Constants.TOKEN, token);
                }
            }
        }
        return ajax;
    }
    /**
     * 单点登录
     *
     * @param code 登录信息
     * @return 结果
     */
    @GetMapping("/login/lxsso")
    @ApiOperation("单点登录接口")
    public AjaxResult lxssoLogin(@RequestParam String code, HttpServletResponse response)
    {
        AjaxResult ajax = AjaxResult.success();
        if(StringUtils.isBlank(code)){
            //code为空直接重定向sso，获取code
            StringBuilder urlSb = new StringBuilder();
            try {
                urlSb.append(lxHost+"/authorize").append("?").append("client_id=").append(lxClientId).append("&response_type=code")
                        .append("&redirect_uri=").append(URLEncoder.encode(lxRedirectUri, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            try {
                response.sendRedirect(urlSb.toString());
                return ajax;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        log.info("单点登录Code:{}", code);
        String appToken = getAppToken();
        if(StringUtils.isBlank(appToken)){
            return AjaxResult.error("获取appToken失败");
        }
        String accessToken = getAccessToken(code, appToken);
        if(StringUtils.isBlank(accessToken)){
            return AjaxResult.error("获取accessToken失败");
        }
        RestTemplate restTemplate = RestTemplateUtil.getRestTemplate();
        ResponseEntity<String> responseData = restTemplate.getForEntity(lxGatewayHost + "/v1/users/fetch?app_token="+appToken+"&user_token=" + accessToken, String.class);
        log.info("单点登录获取用户信息返回结果:{}", responseData.getBody());
        if (responseData.getBody() != null) {
            JSONObject userInfo = JSONObject.parseObject(responseData.getBody()).getJSONObject("data");
            if (userInfo != null && userInfo.getString("employeeNumber") != null) {
                //生成本地token
                String token = loginService.ssoLogin(userInfo.getString("employeeNumber"));
                ajax.put(Constants.TOKEN, token);
            }else{
                return AjaxResult.error("获取用户信息失败,请联系管理员");
            }
        }
        return ajax;
    }

    /**
     * 蓝信发送消息
     *
     * @param userId 蓝信发送消息
     * @return 结果
     */
    @GetMapping("/lxapi/sendTextMessage")
    @ApiOperation("蓝信发送消息")
    public AjaxResult sendTextMessage(@RequestParam String userId, HttpServletResponse response)
    {
        List<String> userIds = new ArrayList<>();
        userIds.add(userId);
        lanXinService.sendTextMessage(userIds, "测试");
        return AjaxResult.success();
    }
    private String getAccessToken(String code, String appToken){
        String userToken = null;
//        String userToken = redisCache.getCacheObject("userToken");
//        if(StringUtils.isNotBlank(userToken)){
//            //从redis缓存中获取userToken
//            return userToken;
//        }
        // 获取人员访问TOKEN
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(lxGatewayHost+"/v1/usertoken/create?app_token=").append(appToken).append("&grant_type=authorization_code").append("&redirect_uri=").append(lxRedirectUri)
                .append("&code=").append(code);
        log.info("单点登录url:{}", urlSb.toString());
        RestTemplate restTemplate = RestTemplateUtil.getRestTemplate();
        ResponseEntity<String> responseData = restTemplate.getForEntity(urlSb.toString(), String.class);
        log.info("单点登录获取token返回结果:{}", responseData.getBody());
        if(responseData.getBody() != null) {
            JSONObject jsonObject = JSONObject.parseObject(responseData.getBody());
            if (jsonObject.getJSONObject("data") != null) {
                userToken = jsonObject.getJSONObject("data").getString("userToken");
                redisCache.setCacheObject("userToken", userToken, 7000, TimeUnit.SECONDS);
            }
        }
        return userToken;
    }

    private String getJsApiToken(){
        String jsApiToken = redisCache.getCacheObject("jsApiToken");
        if(StringUtils.isNotBlank(jsApiToken)){
            //从redis缓存中获取jsApiToken
            log.info("蓝信从缓存中获取jsApiToken：{}", jsApiToken);
            return jsApiToken;
        }
        //获取jsApiToken
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(lxGatewayHost+"/v1/jsapitoken/create?app_token=").append(getAppToken());
        log.info("获取jsApiToken Url:{}", urlSb.toString());
        RestTemplate restTemplate = RestTemplateUtil.getRestTemplate();
        ResponseEntity<String> responseData = restTemplate.getForEntity(urlSb.toString(), String.class);
        log.info("获取jsApiToken:{}", responseData.getBody());
        if(responseData.getBody() != null){
            JSONObject jsonObject = JSONObject.parseObject(responseData.getBody());
            if(jsonObject.getJSONObject("data") != null){
                jsApiToken = jsonObject.getJSONObject("data").getString("jsApiToken");
                redisCache.setCacheObject("jsApiToken", jsApiToken, 7000, TimeUnit.SECONDS);
            }
        }
        return jsApiToken;
    }

    private String getAppToken(){
        String appToken = redisCache.getCacheObject("appToken");
        if(StringUtils.isNotBlank(appToken)){
            //从redis缓存中获取appToken
            return appToken;
        }
        // 获取单点apptoken
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(lxGatewayHost+"/v1/apptoken/create?grant_type=client_credential&appid=").append(lxClientId).append("&secret=").append(lxClientSecret);
        log.info("获取单点apptokenurl:{}", urlSb.toString());
        RestTemplate restTemplate = RestTemplateUtil.getRestTemplate();
        ResponseEntity<String> responseData = restTemplate.getForEntity(urlSb.toString(), String.class);
        log.info("获取单点apptoken返回结果:{}", responseData.getBody());
        if(responseData.getBody() != null){
            JSONObject jsonObject = JSONObject.parseObject(responseData.getBody());
            if(jsonObject.getJSONObject("data") != null){
                appToken = jsonObject.getJSONObject("data").getString("appToken");
                redisCache.setCacheObject("appToken", appToken, 7000, TimeUnit.SECONDS);
            }
        }
        return appToken;
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    @ApiOperation("获取用户信息")
    public AjaxResult getInfo()
    {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }

    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getRouters")
    @ApiOperation("获取路由信息")
    public AjaxResult getRouters()
    {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }
}
